// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <cantor/parser/ast/expr.h>
#include <cantor/parser/ast/dml.h>
#include <cantor/parser/ast/stmt.h>
#include <unordered_map>
#include <turbo/flags/flag.h>
#include <iomanip>
#include <turbo/strings/str_cat.h>
#include <cantor/parser/ast/dql.h>

namespace cantor::parser {
    void DispatchExpr::to_stream(std::ostream &os) const {
        //KCHECK(false) << "EXPR:" << static_cast<int>(expr_type);
    }

    nlohmann::ordered_json DispatchExpr::to_json_impl() const {
        auto j = Node::to_json_impl();
        j["expr_type"] = cantor::pb::ast::ExprNodeType_Name(expr_type);
        j["expr_class"] = cantor::pb::ast::expr_class_to_string(expr_class);
        return j;
    }

    nlohmann::ordered_json RangeVar::to_json() const {
        nlohmann::ordered_json j = DispatchExpr::to_json_impl();
        auto value = nlohmann::ordered_json::array();
        auto ins = nlohmann::ordered_json::array();
        for (auto i = 0; i < indirections.size(); ++i) {
            ins.push_back(indirections[i]->to_json());
        }
        j["indirections"] = ins;
        j["column"] = column->to_json();
        return j;
    }

    void RangeVar::to_stream(std::ostream &os) const {
       column->to_stream(os);
        for (auto i = 0; i < indirections.size(); ++i) {
            indirections[i]->to_stream(os);
        }
    }

    void SubqueryExpr::to_stream(std::ostream &os) const {
        os << "(";
        if (query_stmt != nullptr && is_select(query_stmt)) {
            auto *select_stmt = (SelectStmt *) query_stmt;
            select_stmt->to_stream(os);
        } else if (query_stmt != nullptr && is_union(query_stmt)) {
            auto *union_stmt = (UnionStmt *) query_stmt;
            union_stmt->to_stream(os);
        }
        os << ")";
    }

    void CompareSubqueryExpr::to_stream(std::ostream &os) const {
        static const char *cmp_type_str[] = {"ANY", "SOME", "ALL"};
        left_expr->to_stream(os);
        switch (func_type) {
            case pb::ast::FT_EQ:
            case pb::ast::FT_NE:
            case pb::ast::FT_GT:
            case pb::ast::FT_GE:
            case pb::ast::FT_LT:
            case pb::ast::FT_LE:
                os << FuncExpr::FUNC_STR_MAP[func_type];
                break;
            default:
                break;
        }
        os << cmp_type_str[cmp_type] << " ";
        right_expr->to_stream(os);
    }

    const char *CompareSubqueryExpr::get_func_name() const {
        return FuncExpr::type_to_name(func_type);
    }

    void ExistsSubqueryExpr::to_stream(std::ostream &os) const {
        os << "EXISTS ";
        query_expr->to_stream(os);
    }

    void ConstantExpr::to_stream(std::ostream &os) const {
        if (turbo::get_flag(FLAGS_ast_print_sample)) {
            os << "?";
            return;
        }
        static const char *true_str[] = {"FALSE", "TRUE"};
        switch (literal_type) {
            case pb::ast::CET_INT:
                os << _u.int64_val;
                break;
            case pb::ast::CET_DOUBLE:
                os << std::setprecision(12) << _u.double_val;
                break;
            case pb::ast::CET_STRING:
                os << "'" << _u.str_val.value << "'";
                break;
            case pb::ast::CET_HEX:
                os << "0x" << row_str.value;
                break;
            case pb::ast::CET_BOOL:
                os << true_str[_u.bool_val];
                break;
            case pb::ast::CET_NULL:
                os << "NULL";
                break;
            case pb::ast::CET_PLACE_HOLDER:
                os << "?";
                break;
            case pb::ast::CET_MAXVALUE:
                os << "MAXVALUE";
                break;
            default:
                break;
        }
    }

    nlohmann::ordered_json ConstantExpr::to_json() const {
        nlohmann::ordered_json j = DispatchExpr::to_json_impl();
        j["literal_type"] = pb::ast::ConstantExprType_Name(literal_type);
        switch (literal_type) {
            case pb::ast::CET_INT:
                j["value"] = _u.int64_val;
                break;
            case pb::ast::CET_DOUBLE:
                j["value"] = _u.double_val;
                break;
            case pb::ast::CET_STRING:
                j["value"] = _u.str_val.value;
                break;
            case pb::ast::CET_HEX:
                j["value"] = turbo::str_cat("0x", row_str.value);
                break;
            case pb::ast::CET_BOOL:
                j["value"] = _u.bool_val;
                break;
            case pb::ast::CET_NULL:
                j["value"] = "NULL";
                break;
            case pb::ast::CET_PLACE_HOLDER:
                j["value"] = "?";
                break;
            case pb::ast::CET_MAXVALUE:
                j["value"] = "MAXVALUE";
                break;
            case pb::ast::CET_MINVALUE:
                j["value"] = "MINVALUE";
                break;
            default:
                break;
        }
        return j;
    }

    // select字段时获取表达式名字
    std::string ConstantExpr::to_string() const {
        std::ostringstream os;
        if (turbo::get_flag(FLAGS_ast_print_sample)) {
            os << "?";
            return os.str();
        }
        static const char *true_str[] = {"FALSE", "TRUE"};
        switch (literal_type) {
            case pb::ast::CET_INT:
                os << _u.int64_val;
                break;
            case pb::ast::CET_DOUBLE:
                os << std::setprecision(12) << _u.double_val;
                break;
            case pb::ast::CET_STRING:
                os << _u.str_val.value;
                break;
            case pb::ast::CET_HEX:
                os << "0x" << row_str.value;
                break;
            case pb::ast::CET_BOOL:
                os << true_str[_u.bool_val];
                break;
            case pb::ast::CET_NULL:
                os << "NULL";
                break;
            case pb::ast::CET_PLACE_HOLDER:
                os << "?";
                break;
            case pb::ast::CET_MAXVALUE:
                os << "MAXVALUE";
                break;
            default:
                break;
        }
        return os.str();
    }

    void ConstantExpr::find_placeholder(std::unordered_set<int> &placeholders) {
        if (placeholder_literal_type == pb::ast::CET_PLACE_HOLDER) {
            placeholders.insert(placeholder_id);
        }
    }

    void CommonTableExpr::to_stream(std::ostream &os) const {
        os << name;
        if (column_names.size() > 0) {
            os << "(";
        }
        for (int i = 0; i < column_names.size(); ++i) {
            column_names[i]->to_stream(os);
            if (i != column_names.size() - 1) {
                os << ", ";
            }
        }
        if (column_names.size() > 0) {
            os << ")";
        }
        os << " AS ";
        query_expr->to_stream(os);
    }

    void ConstantExpr::print() const {
        std::cout << "expr:" << static_cast<int>(expr_type) << " lit:";
        switch (literal_type) {
            case pb::ast::CET_INT:
                std::cout << _u.int64_val;
                break;
            case pb::ast::CET_DOUBLE:
                std::cout << _u.double_val;
                break;
            case pb::ast::CET_STRING:
                std::cout << _u.str_val.value;
                break;
            case pb::ast::CET_HEX:
                std::cout << row_str.value;
                break;
            case pb::ast::CET_BOOL:
                std::cout << _u.bool_val;
                break;
            case pb::ast::CET_NULL:
                std::cout << "NULL";
                break;
            case pb::ast::CET_PLACE_HOLDER:
                std::cout << "?(" << _u.int64_val << ")";
                break;
            case pb::ast::CET_MAXVALUE:
                std::cout << "MAXVALUE";
                break;
            default:
                KCHECK(false) << "bad literal type" << pb::ast::ConstantExprType_Name(literal_type);
        }
        std::cout << std::endl;
    }

    IntConstantExpr *ConstantExpr::make_int(const char *str, turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(IntConstantExpr))) IntConstantExpr();
        lit->value = strtoull(str, NULL, 10);
        return lit;
    }

    DoubleConstantExpr *ConstantExpr::make_double(const char *str, turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(DoubleConstantExpr))) DoubleConstantExpr();
        lit->value = strtod(str, NULL);
        return lit;
    }

    BitConstantExpr *ConstantExpr::make_bit(const char *str, size_t len, turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(BitConstantExpr))) BitConstantExpr();
        std::string out_str;
        out_str.reserve(len / 8 + 1);
        size_t pos = len % 8;
        if (pos != 0) {
            out_str.append(1, bit_to_char(str, pos));
        }
        for (; pos < len; pos += 8) {
            out_str.append(1, bit_to_char(str + pos, 8));
        }
        lit->value.strdup(out_str.c_str(), out_str.size(), arena);
        return lit;
    }

    HexConstantExpr *ConstantExpr::make_hex(const char *str, size_t len, turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(HexConstantExpr))) HexConstantExpr();
        std::string out_str;
        out_str.reserve(len / 2 + 1);
        size_t pos = len % 2;
        if (pos != 0) {
            out_str.append(1, hex_to_char(str, pos));
        }
        for (; pos < len; pos += 2) {
            out_str.append(1, hex_to_char(str + pos, 2));
        }
        lit->value.strdup(out_str.c_str(), out_str.size(), arena);
        lit->row_str.strdup(str, len, arena);
        return lit;
    }

    StringConstantExpr *ConstantExpr::make_string(const char *str, turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(StringConstantExpr))) StringConstantExpr();
        /// trim ' "
        if (str[0] == '"' || str[0] == '\'') {
            /// sql "query in ('\x00text', 'text')" ，strlen(str)=1
            auto str_len = strlen(str);
            auto cut_num = (str_len == 1 || str[str_len - 1] != str[0]) ? 1 : 2;
            lit->value.strdup(str + 1, str_len - cut_num, arena);
        } else {
            lit->value.strdup(str, ::strlen(str), arena);
        }
        return lit;
    }

    StringConstantExpr *ConstantExpr::make_string(String value, turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(StringConstantExpr))) StringConstantExpr();
        lit->value = value;
        return lit;
    }

    BoolConstantExpr *ConstantExpr::make_true(turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(BoolConstantExpr))) BoolConstantExpr();
        lit->value = true;
        return lit;
    }

    BoolConstantExpr *ConstantExpr::make_false(turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(BoolConstantExpr))) BoolConstantExpr();
        lit->value = false;
        return lit;
    }

    NullConstantExpr *ConstantExpr::make_null(turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(NullConstantExpr))) NullConstantExpr();
        return lit;
    }

    PlaceholderConstantExpr *ConstantExpr::make_place_holder(int place_holder_id, turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(PlaceholderConstantExpr))) PlaceholderConstantExpr();
        lit->placeholder_id = place_holder_id;
        return lit;
    }

    MaxValueConstantExpr *ConstantExpr::make_maxvalue(turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(MaxValueConstantExpr))) MaxValueConstantExpr();
        return lit;
    }

    MinValueConstantExpr *ConstantExpr::make_minvalue(turbo::Arena &arena) {
        auto *lit = new(arena.allocate(sizeof(MinValueConstantExpr))) MinValueConstantExpr();
        return lit;
    }

    nlohmann::ordered_json RowExpr::to_json() const {
        nlohmann::ordered_json row = DispatchExpr::to_json_impl();
        auto cols = nlohmann::ordered_json::array();
        for (auto i = 0; i < columns.size(); i++) {
            cols.push_back(columns[i]->to_json());
        }
        row["columns"] = cols;
        auto subs = nlohmann::ordered_json::array();
        for (auto i = 0; i < sub_exprs.size(); i++) {
            subs.push_back(sub_exprs[i]->to_json());
        }
        row["sub_exprs"] = subs;
        return row;
    }

    nlohmann::ordered_json DefiniteExpr::to_json_impl() const {
        auto j = Node::to_json_impl();
        j["expr_type"] = cantor::pb::ast::ExprNodeType_Name(expr_type);
        j["expr_class"] = cantor::pb::ast::expr_class_to_string(expr_class);
        return j;
    }
} // namespace cantor::parser
